package org.apache.felix.main;

import com.quickfile.File;
import java.io.PrintStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.service.startlevel.StartLevel;

public class AutoProcessor {
	public static final String AUTO_DEPLOY_DIR_PROPERY = "felix.auto.deploy.dir";
	public static final String AUTO_DEPLOY_DIR_VALUE = "bundle";
	public static final String AUTO_DEPLOY_ACTION_PROPERY = "felix.auto.deploy.action";
	public static final String AUTO_DEPLOY_STARTLEVEL_PROPERY = "felix.auto.deploy.startlevel";
	public static final String AUTO_DEPLOY_INSTALL_VALUE = "install";
	public static final String AUTO_DEPLOY_START_VALUE = "start";
	public static final String AUTO_DEPLOY_UPDATE_VALUE = "update";
	public static final String AUTO_DEPLOY_UNINSTALL_VALUE = "uninstall";
	public static final String AUTO_INSTALL_PROP = "felix.auto.install";
	public static final String AUTO_START_PROP = "felix.auto.start";

	public static void process(Map configMap, BundleContext context) {
		configMap = configMap == null ? new HashMap() : configMap;
		processAutoDeploy(configMap, context);
		processAutoProperties(configMap, context);
	}

	private static void processAutoDeploy(Map configMap, BundleContext context) {
		String action = (String) configMap.get("felix.auto.deploy.action");
		action = action == null ? "" : action;
		List actionList = new ArrayList();
		StringTokenizer st = new StringTokenizer(action, ",");
		while (st.hasMoreTokens()) {
			String s = st.nextToken().trim().toLowerCase();
			if ((s.equals("install")) || (s.equals("start"))
					|| (s.equals("update")) || (s.equals("uninstall"))) {
				actionList.add(s);
			}

		}

		if (actionList.size() > 0) {
			StartLevel sl = (StartLevel) context.getService(context
					.getServiceReference(StartLevel.class.getName()));

			int startLevel = sl.getInitialBundleStartLevel();
			if (configMap.get("felix.auto.deploy.startlevel") != null) {
				try {
					startLevel = Integer.parseInt(configMap.get(
							"felix.auto.deploy.startlevel").toString());
				} catch (NumberFormatException ex) {
				}

			}

			Map installedBundleMap = new HashMap();
			Bundle[] bundles = context.getBundles();
			for (int i = 0; i < bundles.length; i++) {
				installedBundleMap.put(bundles[i].getLocation(), bundles[i]);
			}

			String autoDir = (String) configMap.get("felix.auto.deploy.dir");
			autoDir = autoDir == null ? "bundle" : autoDir;

			File[] files = File.newInstance(autoDir).listFiles();
			List jarList = new ArrayList();
			if (files != null) {
				Arrays.sort(files);
				for (int i = 0; i < files.length; i++) {
					if (!files[i].getName().endsWith(".jar"))
						continue;
					jarList.add(files[i]);
				}

			}

			List startBundleList = new ArrayList();
			for (int i = 0; i < jarList.size(); i++) {
				Bundle b = (Bundle) installedBundleMap.remove(((File) jarList
						.get(i)).toURI().toString());
				try {
					if ((b == null) && (actionList.contains("install"))) {
						b = context.installBundle(((File) jarList.get(i))
								.toURI().toString());
					} else if ((b != null) && (actionList.contains("update"))) {
						b.update();
					}

					if ((b != null) && (!isFragment(b))) {
						startBundleList.add(b);
						sl.setBundleStartLevel(b, startLevel);
					}
				} catch (BundleException ex) {
					System.err.println("Auto-deploy install: "
							+ ex
							+ (ex.getCause() != null ? " - " + ex.getCause()
									: ""));
				}
			}
			Iterator it;
			if (actionList.contains("uninstall")) {
				for (it = installedBundleMap.entrySet().iterator(); it
						.hasNext();) {
					Map.Entry entry = (Map.Entry) it.next();
					Bundle b = (Bundle) entry.getValue();
					if (b.getBundleId() != 0L) {
						try {
							b.uninstall();
						} catch (BundleException ex) {
							System.err.println("Auto-deploy uninstall: "
									+ ex
									+ (ex.getCause() != null ? " - "
											+ ex.getCause() : ""));
						}

					}

				}

			}

			if (actionList.contains("start")) {
				for (int i = 0; i < startBundleList.size(); i++) {
					try {
						((Bundle) startBundleList.get(i)).start();
					} catch (BundleException ex) {
						System.err.println("Auto-deploy start: "
								+ ex
								+ (ex.getCause() != null ? " - "
										+ ex.getCause() : ""));
					}
				}
			}
		}
	}

	private static void processAutoProperties(Map configMap,
			BundleContext context) {
		StartLevel sl = (StartLevel) context.getService(context
				.getServiceReference(StartLevel.class.getName()));

		for (Iterator i = configMap.keySet().iterator(); i.hasNext();) {
			String key = ((String) i.next()).toLowerCase();

			if ((!key.startsWith("felix.auto.install"))
					&& (!key.startsWith("felix.auto.start"))) {
				continue;
			}

			int startLevel = sl.getInitialBundleStartLevel();
			if ((!key.equals("felix.auto.install"))
					&& (!key.equals("felix.auto.start"))) {
				try {
					startLevel = Integer.parseInt(key.substring(key
							.lastIndexOf('.') + 1));
				} catch (NumberFormatException ex) {
					System.err.println("Invalid property: " + key);
				}

			}

			StringTokenizer st = new StringTokenizer(
					(String) configMap.get(key), "\" ", true);
			for (String location = nextLocation(st); location != null; location = nextLocation(st)) {
				try {
					Bundle b = context.installBundle(location, null);
					sl.setBundleStartLevel(b, startLevel);
				} catch (Exception ex) {
					System.err.println("Auto-properties install: "
							+ location
							+ " ("
							+ ex
							+ (ex.getCause() != null ? " - " + ex.getCause()
									: "") + ")");

					if (ex.getCause() != null) {
						ex.printStackTrace();
					}
				}
			}
		}

		for (Iterator i = configMap.keySet().iterator(); i.hasNext();) {
			String key = ((String) i.next()).toLowerCase();
			if (key.startsWith("felix.auto.start")) {
				StringTokenizer st = new StringTokenizer(
						(String) configMap.get(key), "\" ", true);
				for (String location = nextLocation(st); location != null; location = nextLocation(st)) {
					try {
						Bundle b = context.installBundle(location, null);
						if (b != null) {
							b.start();
						}
					} catch (Exception ex) {
						System.err.println("Auto-properties start: "
								+ location
								+ " ("
								+ ex
								+ (ex.getCause() != null ? " - "
										+ ex.getCause() : "") + ")");
					}
				}
			}
		}
	}

	private static String nextLocation(StringTokenizer st) {
		String retVal = null;

		if (st.countTokens() > 0) {
			String tokenList = "\" ";
			StringBuffer tokBuf = new StringBuffer(10);
			String tok = null;
			boolean inQuote = false;
			boolean tokStarted = false;
			boolean exit = false;
			while ((st.hasMoreTokens()) && (!exit)) {
				tok = st.nextToken(tokenList);
				if (tok.equals("\"")) {
					inQuote = !inQuote;
					if (inQuote) {
						tokenList = "\"";
						continue;
					}

					tokenList = "\" ";
					continue;
				}

				if (tok.equals(" ")) {
					if (!tokStarted)
						continue;
					retVal = tokBuf.toString();
					tokStarted = false;
					tokBuf = new StringBuffer(10);
					exit = true;
					continue;
				}

				tokStarted = true;
				tokBuf.append(tok.trim());
			}

			if ((!exit) && (tokStarted)) {
				retVal = tokBuf.toString();
			}
		}

		return retVal;
	}

	private static boolean isFragment(Bundle bundle) {
		return bundle.getHeaders().get("Fragment-Host") != null;
	}
}